package amushen.base;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * This is a action for MVC.
 * Using with flex together, all functions:
 * 	 1.receive request from flex , then dispatch to special BO
 *   2.doing page query automatic
 * @author shen.n 
 */
public class BaseAction extends HttpServlet {

	private static final long serialVersionUID = -6179738704041289704L;
	
	/**
	 * prePaht&{cmd} to find a real class
	 */
	private static String PrePath;
	public static String RealRootPath;
	
	//use this save db connection for every session
//	public static ThreadLocal DBUtils=new ThreadLocal();  
	
	public static final ThreadLocal httpSession = new ThreadLocal(); 
	
	public static Logger logger=Logger.getLogger(BaseAction.class.getName());
	
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		System.out.println("-----------------------------------");
		System.out.println(config.getInitParameter("APP_NAME")+" starting...");
		System.out.println("Author: shen.nan(amushen@gmail.com)");
		System.out.println("-----------------------------------");
		RealRootPath=this.getServletContext().getRealPath("/");
		logger.log(Level.INFO, "Root:"+RealRootPath);
		PrePath=this.getServletConfig().getInitParameter("PRE_PATH");
		logger.log(Level.INFO, "PRE:"+PrePath);
	}
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {			
		doPost(req,resp);
	}
	
	/**
	 * deal all request from client
	 * input e.g.: /action?cmd=User.delete&type=[text(include flex),image,download]
	 * type is optional default is text
	 * 
	 * this will invoke amushen.module.User class's delete method
	 * all method need have three parameter:paras:Map,user:String,is:InputStream
	 * 	 - paras is all parameter in request
	 *   - user is "UserName" in Session, when to use,need split with ","
	 *   - is:inuptstream when input a stream  (optional)
	 * and need to return an Object
	 */
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {	
		String cmd,type;	
		cmd=req.getParameter("cmd");
		type=req.getParameter("type");	
		
		//avoid IE cache the result
		resp.setHeader("Pragma","No-cache"); 
		resp.setHeader("Cache-Control","no-cache"); 
		resp.setDateHeader("Expires", 0); 
		
		//resp.setCharacterEncoding("UTF-8");		
		
		if(cmd==null||cmd.length()<1){	//not contains command for deal with
			//error request			
			resp.setStatus(500);
			resp.getWriter().println("Please give me a command.");
			return;
		}
		if(type==null||type.length()<1)	type="text";
		String username=(String)req.getSession().getAttribute("UserName");
		if(cmd.indexOf("Login")<0&&(username==null||username.length()<1)){
			resp.setStatus(401);
			resp.getWriter().println("Please login in.");
			return;
		}
		//get Parameters
		Map paras=getRequestParameter(req);
		
		//get BO
		String boClazz;
		String boMethod;		
		int ind=cmd.lastIndexOf(".");
		if(ind<0){//no method
			boClazz=cmd;
			boMethod="defaultMethod";
		}else{
			boClazz=cmd.substring(0, ind);
			boMethod=cmd.substring(ind+1);
			if(boMethod.charAt(0)>='A'&&boMethod.charAt(0)<='Z'){
				//last name isn't method name.
				boClazz=cmd;
				boMethod="defaultMethod";				
			}
		}
		logger.log(Level.INFO,"BO.Method="+PrePath+"."+boClazz+"."+boMethod);
		//invoke bo method
		Object ret;		
		DBUtil db=new DBUtil();		
		httpSession.set(req.getSession());
		try{
			Class bo=Class.forName(PrePath+"."+boClazz);
			Object BO=bo.newInstance();
			if(BO instanceof BaseBO){
				Method method=bo.getMethod(boMethod, new Class[]{Map.class,String.class,InputStream.class,DBUtil.class});
				ret=method.invoke(BO, new Object[]{paras,username,req.getInputStream(),db});
			}else{
				logger.log(Level.WARNING,"Can't run BO method,not real BO,cmd="+cmd);
				resp.setStatus(500);
				resp.getWriter().println("Can't run BO method,not real BO,cmd="+cmd);
				return;
			}			
		}catch(ClassNotFoundException cnfe){
			logger.log(Level.WARNING,"Can't find BO,cmd="+cmd);
			resp.setStatus(500);
			resp.getWriter().println("Can't find BO,cmd="+cmd);
			httpSession.set(null);
			db.close();
			return;
		}catch(NoSuchMethodException nsme){
			logger.log(Level.WARNING,"Can't find BO method,cmd="+cmd);
			resp.setStatus(500);
			resp.getWriter().println("Can't find BO method,cmd="+cmd);
			httpSession.set(null);
			db.close();
			return;
		}catch(Exception e){
			e.printStackTrace();
			logger.log(Level.WARNING,"Can't run BO method,cmd="+cmd+","+e.getMessage());
			resp.setStatus(500);
			resp.getWriter().println("Can't run BO method,cmd="+cmd);
			httpSession.set(null);
			db.close();
			return;
		}
		httpSession.set(null);
		db.close();
		
		//deal with return 
		if(ret==null)return;
		byte[] b=new byte[255];
		int r;
		BufferedInputStream bis;
		if(type.equalsIgnoreCase("text")){//text or json
			resp.setContentType("text/html; charset=utf-8");
			resp.getWriter().print((String)ret);
			return;
		}else if(type.equalsIgnoreCase("image")){//return a image
			ReturnImage ri=(ReturnImage)ret;
			resp.setContentType("image/"+ri.type);
			//write image
			bis=new BufferedInputStream(ri.is);			
			while((r=bis.read(b))>0){
				resp.getOutputStream().write(b,0,r);
				resp.flushBuffer();
			}
			bis.close();
			return;
		}else if(type.equalsIgnoreCase("download")){//return a down load such as excel
			ReturnFile rf=(ReturnFile)ret; 
			resp.setContentType("APPLICATION/OCTET-STREAM");   
		    resp.setHeader("Content-Disposition","attachment;filename=\"" + rf.filename + "\"");
		    bis=new BufferedInputStream(rf.is);
			while((r=bis.read(b))>0){
				resp.getOutputStream().write(b,0,r);
				resp.flushBuffer();
			}
			bis.close();
			return;		     
		}else{
			logger.log(Level.WARNING,"error type,type="+type);
			resp.setStatus(500);
			resp.getWriter().println("error type,type="+type);
			return;
		}
		//
		
	}
	
	/**
	 * colon request's parameter , in order to pass it to BO
	 * @param req
	 * @return
	 */
	private Map getRequestParameter(HttpServletRequest req){		
		//clone the request's map
		HashMap hm=new HashMap();
		Map r=req.getParameterMap();
		Iterator i=r.keySet().iterator();
		String key;
		while(i.hasNext()){
			key=(String)i.next();
			hm.put(key, req.getParameter(key));
		}
		return hm;
	}
	
	/**
	 * using for bo method return.
	 * As the return Object
	 * @author limeng
	 *
	 */
	private static class ReturnFile{
		public String filename;
		public InputStream is;		
	}
	private static class ReturnImage{
		public String type="jpeg";
		public InputStream is;
	}
	
}
